package Messages;

import jade.core.Agent;
import jade.domain.AMSService;
import jade.domain.FIPAAgentManagement.AMSAgentDescription;
import jade.domain.FIPAAgentManagement.SearchConstraints;
import jade.lang.acl.ACLMessage;
import Agents.Messageable;
import Messages.OfficeRegionMessages.AdjustToUser;

public abstract class AMessage 
{
	/* ************** Items used in different messages ***************** */
	protected static final String senderSplit = " SENDS ";
	protected static final String unitSplit = " IS ";
	protected static final String unitsSplit = " , ";
	protected static final String messageSplit = " = ";
	protected static final String typeSplit = " TYPE ";
	public 	  static final long unitsManipulatingConstant = 250; // 1 sec     1000/3
	public    static final long userInformConstant = 10000; // 10 sec
	/* ***************************************************************** */
	
	/* ************** Vacuum cleaner specialized items ***************** */
	public	static enum VacuumStatus { VACUUMING, NEED_POWER, CHARGING, IDLE, EMPTING_BIN };
	/* ***************************************************************** */
	
	/* ************** MeasureMessage's specialized items *************** */
	public	static enum  Units { TEMPERATURE, NOISE, LIGHT, VOLUME, WEIGHT, LOCATION_X, LOCATION_Y, VACUUM_CLEANER, FLOOR_PCT_DIRTY, VACUUM_CLEANER_TIMETOUSE };
	public	static enum  GlobalNames 
		{
			VACUUM_CLEANER_AGENT("VacuumCleanerAgent"), 
			OFFICE_REGION_AGENT("OfficeRegionAgent"), 
			USER_AGENT("UserAgent"),
			FLOOR_HEATER_AGENT("FloorHeaterAgent"),
			TEMPERATURE_SENSOR_AGENT("TemperatureSensorAgent"),
			BIN_AGENT("BinAgent"),
			
			DEFAULT("default");
	
			private String p_representation;
	
			GlobalNames(String representation)
			{
				this.p_representation = representation;
			}
	
			public String get()
			{
				return this.p_representation;
			}
			
			public static GlobalNames decode(String localName)
			{
				for(GlobalNames pr : values())
				{
					if(pr.p_representation.equalsIgnoreCase(localName.substring(2)))
						return pr;
				}
				return DEFAULT;
			}
			
			public static GlobalNames toEnum(String globalName)
			{
				for(GlobalNames pr : values())
				{
					if(pr.p_representation.equalsIgnoreCase(globalName))
						return pr;
				}
				return DEFAULT;
			}

			public static String getAreaCode(String localName) 
			{
				return localName.substring(0,2);
			}
		};
	/* ***************************************************************** */
	
	public static String[] worlds = { "11World","21World","12World","22World"};
	public static enum MessageType 
		{ 
			MEASUREMENT("Measurement"), /* Message type for the Floorheater to inform the world, the TemperatureSensor to measure from the world.  */
			USER("User"), /* Message type for the User for inform its location and preferences */
			VACUUM_CLEANER("Vacuum_cleaner"),
			EMIT("Emitting"),
			ADJUST("Do_adjust"),
			NOTIFY_ONLY("Notify_only"),
			CALL_CLEAN("Calling_for_cleanup"),
			CALL_EMPTY_BIN("Calling_for_emptying_of_bin"),
			PROPOSE_CLEAN("Offer_to_clean"),
			PROPOSE_EMPTY_BIN("Offer_to_empty_bin"),
			I_NEED_POWER("VC_needs_power"),
			
			DEFAULT("default");
			
			private String p_representation;
			
			MessageType(String representation)
			{
				this.p_representation = representation;
			}
			
			public String get()
			{
				return this.p_representation;
			}
			public static MessageType decode(String type)
			{
				for(MessageType pr : values())
				{
					if(pr.p_representation.equalsIgnoreCase(type))
						return pr;
				}
				return DEFAULT;
			}
		};
		
	protected String sender;
	private MessageType messageType;
	
	public String getSender()
	{
		return sender;
	}
	
	public MessageType getMessageType()
	{
		return messageType;
	}
	
	public static String constructMessage(Messageable agent, MessageType type)
	{
		String returnerString = null;
		AMessage am;
		switch(type)
		{
			case MEASUREMENT:
				am = new MeasureMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case EMIT:
				am = new EmitMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case ADJUST:
				am = new AdjustToUser();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case USER:
				am = new UserMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case VACUUM_CLEANER:
				am = new VacuumCleanerMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case NOTIFY_ONLY:
				am = new NotifyMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			case CALL_CLEAN:
				am = new VacuumCleanerMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent, type);
				break;
			case PROPOSE_CLEAN:
				am = new VacuumCleanerMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent, type);
				break;
			case CALL_EMPTY_BIN:
				am = new BinMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent, type);
				break;
			case PROPOSE_EMPTY_BIN:
				am = new VacuumCleanerMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent, type);
				break;
			case I_NEED_POWER:
				am = new VacuumCleanerMessage();
				returnerString = type.get() + AMessage.messageSplit + am.constructSpecialMessage(agent);
				break;
			default:
		}
		return returnerString;
	}
	
	public static AMessage retrieveMessage(String aMessage)
	{
		AMessage message = null;
		String[] information = aMessage.split(AMessage.messageSplit);
		MessageType type = MessageType.decode(information[0]);
		String theMessage = information[1];
		switch(type)
		{
			case MEASUREMENT:
				message = new MeasureMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case EMIT:
				message = new EmitMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case ADJUST:
				message = new AdjustToUser();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case USER:
				message = new UserMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case VACUUM_CLEANER:
				message = new VacuumCleanerMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case NOTIFY_ONLY:
				message = new NotifyMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case CALL_CLEAN:
				message = new VacuumCleanerMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case PROPOSE_CLEAN:
				message = new VacuumCleanerMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case CALL_EMPTY_BIN:
				message = new BinMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case PROPOSE_EMPTY_BIN:
				message = new VacuumCleanerMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			case I_NEED_POWER:
				message = new VacuumCleanerMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
				break;
			default:
				message = new NotUnderstoodMessage();
				message.messageType = type;
				((AMessage)message).retrieveMyMessage(theMessage);
		}

		return message;
	}
	
	public static void broadcastToAll(Messageable agent, ACLMessage acl)
	{
		AMSAgentDescription [] agents = null;
      	try 
      	{
            SearchConstraints c = new SearchConstraints();
            c.setMaxResults (new Long(-1));
			agents = AMSService.search( (Agent)agent, new AMSAgentDescription (), c );
			agents = AMSService.search( (Agent)agent, new AMSAgentDescription (), c );
			for (int i=0; i<agents.length;i++)
			{
				if( !agents[i].getName().getLocalName().equalsIgnoreCase(((Agent)agent).getAID().getLocalName()) )
					acl.addReceiver( agents[i].getName() ); 
			}
			((Agent)agent).send(acl);
		}
		catch (Exception e) 
		{
            System.out.println( "Problem searching AMS: " + e );
            e.printStackTrace();
		}
	}

	protected abstract String constructSpecialMessage(Messageable agent);
	protected abstract String constructSpecialMessage(Messageable agent, MessageType type);
	protected abstract void retrieveMyMessage(String theMessage);
	
	protected Units getUnit(String unit)
	{
		Units u;
		if(unit.equalsIgnoreCase(Units.LIGHT.name()))
		{
			u = Units.LIGHT;
		}
		else if(unit.equalsIgnoreCase(Units.NOISE.name()))
		{
			u = Units.NOISE;
		}
		else if(unit.equalsIgnoreCase(Units.TEMPERATURE.name()))
		{
			u = Units.TEMPERATURE;
		}
		else if(unit.equalsIgnoreCase(Units.VOLUME.name()))
		{
			u = Units.VOLUME;
		}
		else if(unit.equalsIgnoreCase(Units.WEIGHT.name()))
		{
			u = Units.WEIGHT;
		}
		else
		{
			u = null;
		}
		return u;
	}
	
}

//public enum Programs
//{
//	ROTATE(0),
//	ROTATE_CONTINUOUS(1),
//	ROTATE_DEGREE(2),
//	ROTATE_TO_RADIAN(3),
//	ROTATE_TO_DEGREE(4),
//	ROTATE_DIR_DEGREE(5),
//	ROTATE_VIA_DEGREE(6),
//
//	ROTATE_REVERSE(10),
//	ROTATE_REVERSE_CONTINUOUS(11),
//	ROTATE_REVERSE_DEGREE(12),
//	ROTATE_REVERSE_TO_RADIAN(13),
//	ROTATE_REVERSE_TO_DEGREE(14),
//	ROTATE_REVERSE_DIR_DEGREE(15),
//	ROTATE_REVERSE_VIA_DEGREE(16),
//	STOP_ROTATE(7),
//	STOP_EMERGENCY(17),
//	
//	MOVE_FORWARD_CONTINUOUS(20),
//	MOVE_FORWARD_DEGREE(21),
//	MOVE_BACKWARD_CONTINUOUS(22),
//	MOVE_BACKWARD_DEGREE(23),
//	TURN_LEFT_DEGREE(24),
//	TURN_RIGHT_DEGREE(25),
//	STOP_MOVE(26),
//	
//	DEFAULT(999);
//	
//	private int p_representation;
//	
//	Programs(int representation)
//	{
//		this.p_representation = representation;
//	}
//	
//	public int get()
//	{
//		return this.p_representation;
//	}
//	public static Programs decode(int program)
//	{
//		for(Programs pr : values()){
//			if(pr.p_representation == program)
//				return pr;
//		}
//		return DEFAULT;
//	}
//}
